/*
 * Copyright 1999-2017 Alibaba Group.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.fastjson.parser;

import static com.alibaba.fastjson.parser.JSONLexer.EOI;
import static com.alibaba.fastjson.parser.JSONToken.EOF;
import static com.alibaba.fastjson.parser.JSONToken.ERROR;
import static com.alibaba.fastjson.parser.JSONToken.FALSE;
import static com.alibaba.fastjson.parser.JSONToken.LBRACE;
import static com.alibaba.fastjson.parser.JSONToken.LBRACKET;
import static com.alibaba.fastjson.parser.JSONToken.LITERAL_FLOAT;
import static com.alibaba.fastjson.parser.JSONToken.LITERAL_INT;
import static com.alibaba.fastjson.parser.JSONToken.LITERAL_STRING;
import static com.alibaba.fastjson.parser.JSONToken.NEW;
import static com.alibaba.fastjson.parser.JSONToken.NULL;
import static com.alibaba.fastjson.parser.JSONToken.RBRACKET;
import static com.alibaba.fastjson.parser.JSONToken.SET;
import static com.alibaba.fastjson.parser.JSONToken.TREE_SET;
import static com.alibaba.fastjson.parser.JSONToken.TRUE;
import static com.alibaba.fastjson.parser.JSONToken.UNDEFINED;

import java.io.Closeable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.alibaba.fastjson.*;
import com.alibaba.fastjson.parser.deserializer.ExtraProcessable;
import com.alibaba.fastjson.parser.deserializer.ExtraProcessor;
import com.alibaba.fastjson.parser.deserializer.ExtraTypeProvider;
import com.alibaba.fastjson.parser.deserializer.FieldDeserializer;
import com.alibaba.fastjson.parser.deserializer.FieldTypeResolver;
import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer;
import com.alibaba.fastjson.parser.deserializer.MapDeserializer;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.parser.deserializer.ResolveFieldDeserializer;
import com.alibaba.fastjson.serializer.*;
import com.alibaba.fastjson.util.TypeUtils;

/**
 * @author wenshao[szujobs@hotmail.com]
 */
public class DefaultJSONParser implements Closeable {

	public final Object input;
	public final SymbolTable symbolTable;
	protected ParserConfig config;

	private final static Set<Class<?>> primitiveClasses = new HashSet<Class<?>>();

	private String dateFormatPattern = JSON.DEFFAULT_DATE_FORMAT;
	private DateFormat dateFormat;

	public final JSONLexer lexer;

	protected ParseContext context;

	private ParseContext[] contextArray;
	private int contextArrayIndex = 0;

	private List<ResolveTask> resolveTaskList;

	public final static int NONE = 0;
	public final static int NeedToResolve = 1;
	public final static int TypeNameRedirect = 2;

	public int resolveStatus = NONE;

	private List<ExtraTypeProvider> extraTypeProviders = null;
	private List<ExtraProcessor> extraProcessors = null;
	protected FieldTypeResolver fieldTypeResolver = null;

	private boolean autoTypeEnable;
	private String[] autoTypeAccept = null;

	protected transient BeanContext lastBeanContext;

	static {
		Class<?>[] classes = new Class[] { boolean.class, byte.class, short.class, int.class, long.class, float.class,
				double.class,

				Boolean.class, Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class,

				BigInteger.class, BigDecimal.class, String.class };

		for (Class<?> clazz : classes) {
			primitiveClasses.add(clazz);
		}
	}

	public String getDateFomartPattern() {
		return dateFormatPattern;
	}

	public DateFormat getDateFormat() {
		if (dateFormat == null) {
			dateFormat = new SimpleDateFormat(dateFormatPattern, lexer.getLocale());
			dateFormat.setTimeZone(lexer.getTimeZone());
		}
		return dateFormat;
	}

	public void setDateFormat(String dateFormat) {
		this.dateFormatPattern = dateFormat;
		this.dateFormat = null;
	}

	public void setDateFomrat(DateFormat dateFormat) {
		this.dateFormat = dateFormat;
	}

	public DefaultJSONParser(String input) {
		this(input, ParserConfig.getGlobalInstance(), JSON.DEFAULT_PARSER_FEATURE);
	}

	public DefaultJSONParser(final String input, final ParserConfig config) {
		this(input, new JSONScanner(input, JSON.DEFAULT_PARSER_FEATURE), config);
	}

	public DefaultJSONParser(final String input, final ParserConfig config, int features) {
		this(input, new JSONScanner(input, features), config);
	}

	public DefaultJSONParser(final char[] input, int length, final ParserConfig config, int features) {
		this(input, new JSONScanner(input, length, features), config);
	}

	public DefaultJSONParser(final JSONLexer lexer) {
		this(lexer, ParserConfig.getGlobalInstance());
	}

	public DefaultJSONParser(final JSONLexer lexer, final ParserConfig config) {
		this(null, lexer, config);
	}

	public DefaultJSONParser(final Object input, final JSONLexer lexer, final ParserConfig config) {
		this.lexer = lexer;
		this.input = input;
		this.config = config;
		this.symbolTable = config.symbolTable;

		int ch = lexer.getCurrent();
		if (ch == '{') {
			lexer.next();
			((JSONLexerBase) lexer).token = JSONToken.LBRACE;
		} else if (ch == '[') {
			lexer.next();
			((JSONLexerBase) lexer).token = JSONToken.LBRACKET;
		} else {
			lexer.nextToken(); // prime the pump
		}
	}

	public SymbolTable getSymbolTable() {
		return symbolTable;
	}

	public String getInput() {
		if (input instanceof char[]) {
			return new String((char[]) input);
		}
		return input.toString();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public final Object parseObject(final Map object, Object fieldName) {
		final JSONLexer lexer = this.lexer;

		if (lexer.token() == JSONToken.NULL) {
			lexer.nextToken();
			return null;
		}

		if (lexer.token() == JSONToken.RBRACE) {
			lexer.nextToken();
			return object;
		}

		if (lexer.token() != JSONToken.LBRACE && lexer.token() != JSONToken.COMMA) {
			throw new JSONException("syntax error, expect {, actual " + lexer.tokenName() + ", " + lexer.info());
		}

		ParseContext context = this.context;
		try {
			boolean setContextFlag = false;
			for (;;) {
				lexer.skipWhitespace();
				char ch = lexer.getCurrent();
				if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
					while (ch == ',') {
						lexer.next();
						lexer.skipWhitespace();
						ch = lexer.getCurrent();
					}
				}

				boolean isObjectKey = false;
				Object key;
				if (ch == '"') {
					key = lexer.scanSymbol(symbolTable, '"');
					lexer.skipWhitespace();
					ch = lexer.getCurrent();
					if (ch != ':') {
						throw new JSONException("expect ':' at " + lexer.pos() + ", name " + key);
					}
				} else if (ch == '}') {
					lexer.next();
					lexer.resetStringPosition();
					lexer.nextToken();

					if (!setContextFlag) {
						if (this.context != null && fieldName == this.context.fieldName
								&& object == this.context.object) {
							context = this.context;
						} else {
							ParseContext contextR = setContext(object, fieldName);
							if (context == null) {
								context = contextR;
							}
							setContextFlag = true;
						}
					}

					return object;
				} else if (ch == '\'') {
					if (!lexer.isEnabled(Feature.AllowSingleQuotes)) {
						throw new JSONException("syntax error");
					}

					key = lexer.scanSymbol(symbolTable, '\'');
					lexer.skipWhitespace();
					ch = lexer.getCurrent();
					if (ch != ':') {
						throw new JSONException("expect ':' at " + lexer.pos());
					}
				} else if (ch == EOI) {
					throw new JSONException("syntax error");
				} else if (ch == ',') {
					throw new JSONException("syntax error");
				} else if ((ch >= '0' && ch <= '9') || ch == '-') {
					lexer.resetStringPosition();
					lexer.scanNumber();
					try {
						if (lexer.token() == JSONToken.LITERAL_INT) {
							key = lexer.integerValue();
						} else {
							key = lexer.decimalValue(true);
						}
					} catch (NumberFormatException e) {
						throw new JSONException("parse number key error" + lexer.info());
					}
					ch = lexer.getCurrent();
					if (ch != ':') {
						throw new JSONException("parse number key error" + lexer.info());
					}
				} else if (ch == '{' || ch == '[') {
					lexer.nextToken();
					key = parse();
					isObjectKey = true;
				} else {
					if (!lexer.isEnabled(Feature.AllowUnQuotedFieldNames)) {
						throw new JSONException("syntax error");
					}

					key = lexer.scanSymbolUnQuoted(symbolTable);
					lexer.skipWhitespace();
					ch = lexer.getCurrent();
					if (ch != ':') {
						throw new JSONException("expect ':' at " + lexer.pos() + ", actual " + ch);
					}
				}

				if (!isObjectKey) {
					lexer.next();
					lexer.skipWhitespace();
				}

				ch = lexer.getCurrent();

				lexer.resetStringPosition();

				if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
					String typeName = lexer.scanSymbol(symbolTable, '"');
					Class<?> clazz = config.checkAutoType(typeName, null);

					if (clazz == null) {
						object.put(JSON.DEFAULT_TYPE_KEY, typeName);
						continue;
					}

					lexer.nextToken(JSONToken.COMMA);
					if (lexer.token() == JSONToken.RBRACE) {
						lexer.nextToken(JSONToken.COMMA);
						try {
							Object instance = null;
							ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
							if (deserializer instanceof JavaBeanDeserializer) {
								instance = ((JavaBeanDeserializer) deserializer).createInstance(this, clazz);
							}

							if (instance == null) {
								if (clazz == Cloneable.class) {
									instance = new HashMap();
								} else if ("java.util.Collections$EmptyMap".equals(typeName)) {
									instance = Collections.emptyMap();
								} else {
									instance = clazz.newInstance();
								}
							}

							return instance;
						} catch (Exception e) {
							throw new JSONException("create instance error", e);
						}
					}

					this.setResolveStatus(TypeNameRedirect);

					if (this.context != null && !(fieldName instanceof Integer)
							&& !(this.context.fieldName instanceof Integer)) {
						this.popContext();
					}

					if (object.size() > 0) {
						Object newObj = TypeUtils.cast(object, clazz, this.config);
						this.parseObject(newObj);
						return newObj;
					}

					ObjectDeserializer deserializer = config.getDeserializer(clazz);
					return deserializer.deserialze(this, clazz, fieldName);
				}

				if (key == "$ref" && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
					lexer.nextToken(JSONToken.LITERAL_STRING);
					if (lexer.token() == JSONToken.LITERAL_STRING) {
						String ref = lexer.stringVal();
						lexer.nextToken(JSONToken.RBRACE);

						Object refValue = null;
						if ("@".equals(ref)) {
							if (this.context != null) {
								ParseContext thisContext = this.context;
								Object thisObj = thisContext.object;
								if (thisObj instanceof Object[] || thisObj instanceof Collection<?>) {
									refValue = thisObj;
								} else if (thisContext.parent != null) {
									refValue = thisContext.parent.object;
								}
							}
						} else if ("..".equals(ref)) {
							if (context.object != null) {
								refValue = context.object;
							} else {
								addResolveTask(new ResolveTask(context, ref));
								setResolveStatus(DefaultJSONParser.NeedToResolve);
							}
						} else if ("$".equals(ref)) {
							ParseContext rootContext = context;
							while (rootContext.parent != null) {
								rootContext = rootContext.parent;
							}

							if (rootContext.object != null) {
								refValue = rootContext.object;
							} else {
								addResolveTask(new ResolveTask(rootContext, ref));
								setResolveStatus(DefaultJSONParser.NeedToResolve);
							}
						} else {
							addResolveTask(new ResolveTask(context, ref));
							setResolveStatus(DefaultJSONParser.NeedToResolve);
						}

						if (lexer.token() != JSONToken.RBRACE) {
							throw new JSONException("syntax error");
						}
						lexer.nextToken(JSONToken.COMMA);

						return refValue;
					} else {
						throw new JSONException("illegal ref, " + JSONToken.name(lexer.token()));
					}
				}

				if (!setContextFlag) {
					if (this.context != null && fieldName == this.context.fieldName && object == this.context.object) {
						context = this.context;
					} else {
						ParseContext contextR = setContext(object, fieldName);
						if (context == null) {
							context = contextR;
						}
						setContextFlag = true;
					}
				}

//				if (object.getClass() == JSONObject.class) {
//					key = (key == null) ? "null" : key.toString();
//				}
				key = (key == null) ? "null" : key;

				Object value;
				if (ch == '"') {
					lexer.scanString();
					String strValue = lexer.stringVal();
					value = strValue;

					if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
						JSONScanner iso8601Lexer = new JSONScanner(strValue);
						if (iso8601Lexer.scanISO8601DateIfMatch()) {
							value = iso8601Lexer.getCalendar().getTime();
						}
						iso8601Lexer.close();
					}

					object.put(key, value);
				} else if (ch >= '0' && ch <= '9' || ch == '-') {
					lexer.scanNumber();
					if (lexer.token() == JSONToken.LITERAL_INT) {
						value = lexer.integerValue();
					} else {
						value = lexer.decimalValue(lexer.isEnabled(Feature.UseBigDecimal));
					}

					object.put(key, value);
				} else if (ch == '[') { // 减少嵌套，兼容android
					lexer.nextToken();

					JSONArray list = new JSONArray();

					final boolean parentIsArray = fieldName != null && fieldName.getClass() == Integer.class;
					// if (!parentIsArray) {
					// this.setContext(context);
					// }
					if (fieldName == null) {
						this.setContext(context);
					}

					this.parseArray(list, key);

					if (lexer.isEnabled(Feature.UseObjectArray)) {
						value = list.toArray();
					} else {
						value = list;
					}
					object.put(key, value);

					if (lexer.token() == JSONToken.RBRACE) {
						lexer.nextToken();
						return object;
					} else if (lexer.token() == JSONToken.COMMA) {
						continue;
					} else {
						throw new JSONException("syntax error");
					}
				} else if (ch == '{') { // 减少嵌套，兼容android
					lexer.nextToken();

					final boolean parentIsArray = fieldName != null && fieldName.getClass() == Integer.class;

					JSONObject input = new JSONObject(lexer.isEnabled(Feature.OrderedField));
					ParseContext ctxLocal = null;

					if (!parentIsArray) {
						ctxLocal = setContext(context, input, key);
					}

					Object obj = null;
					boolean objParsed = false;
					if (fieldTypeResolver != null) {
						String resolveFieldName = key != null ? key.toString() : null;
						Type fieldType = fieldTypeResolver.resolve(object, resolveFieldName);
						if (fieldType != null) {
							ObjectDeserializer fieldDeser = config.getDeserializer(fieldType);
							obj = fieldDeser.deserialze(this, fieldType, key);
							objParsed = true;
						}
					} 
					if (!objParsed) {
						obj = this.parseObject(input, key);
					}

					if (ctxLocal != null && input != obj) {
						ctxLocal.object = object;
					}

					checkMapResolve(object, key);

//					if (object.getClass() == JSONObject.class) {
//						object.put(key.toString(), obj);
//					} else {
//						object.put(key, obj);
//					}
					object.put(key, obj);
					if (parentIsArray) {
						// setContext(context, obj, key);
						setContext(obj, key);
					}

					if (lexer.token() == JSONToken.RBRACE) {
						lexer.nextToken();

						setContext(context);
						return object;
					} else if (lexer.token() == JSONToken.COMMA) {
						if (parentIsArray) {
							this.popContext();
						} else {
							this.setContext(context);
						}
						continue;
					} else {
						throw new JSONException("syntax error, " + lexer.tokenName());
					}
				} else {
					lexer.nextToken();
					value = parse();

//					if (object.getClass() == JSONObject.class) {
//						key = key.toString();
//					}
					object.put(key, value);

					if (lexer.token() == JSONToken.RBRACE) {
						lexer.nextToken();
						return object;
					} else if (lexer.token() == JSONToken.COMMA) {
						continue;
					} else {
						throw new JSONException("syntax error, position at " + lexer.pos() + ", name " + key);
					}
				}

				lexer.skipWhitespace();
				ch = lexer.getCurrent();
				if (ch == ',') {
					lexer.next();
					continue;
				} else if (ch == '}') {
					lexer.next();
					lexer.resetStringPosition();
					lexer.nextToken();

					// this.setContext(object, fieldName);
					this.setContext(value, key);

					return object;
				} else {
					throw new JSONException("syntax error, position at " + lexer.pos() + ", name " + key);
				}

			}
		} finally {
			this.setContext(context);
		}

	}

	public ParserConfig getConfig() {
		return config;
	}

	public void setConfig(ParserConfig config) {
		this.config = config;
	}

	// compatible
	@SuppressWarnings("unchecked")
	public <T> T parseObject(Class<T> clazz) {
		return (T) parseObject(clazz, null);
	}

	public <T> T parseObject(Type type) {
		return parseObject(type, null);
	}

	@SuppressWarnings("unchecked")
	public <T> T parseObject(Type type, Object fieldName) {
		int token = lexer.token();
		if (token == JSONToken.NULL) {
			lexer.nextToken();
			return null;
		}

		if (token == JSONToken.LITERAL_STRING) {
			if (type == byte[].class) {
				byte[] bytes = lexer.bytesValue();
				lexer.nextToken();
				return (T) bytes;
			}

			if (type == char[].class) {
				String strVal = lexer.stringVal();
				lexer.nextToken();
				return (T) strVal.toCharArray();
			}
		}

		ObjectDeserializer derializer = config.getDeserializer(type);

		try {
			return (T) derializer.deserialze(this, type, fieldName);
		} catch (JSONException e) {
			throw e;
		} catch (Throwable e) {
			throw new JSONException(e.getMessage(), e);
		}
	}

	public <T> List<T> parseArray(Class<T> clazz) {
		List<T> array = new ArrayList<T>();
		parseArray(clazz, array);
		return array;
	}

	public void parseArray(Class<?> clazz, @SuppressWarnings("rawtypes") Collection array) {
		parseArray((Type) clazz, array);
	}

	@SuppressWarnings("rawtypes")
	public void parseArray(Type type, Collection array) {
		parseArray(type, array, null);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void parseArray(Type type, Collection array, Object fieldName) {
		if (lexer.token() == JSONToken.SET || lexer.token() == JSONToken.TREE_SET) {
			lexer.nextToken();
		}

		if (lexer.token() != JSONToken.LBRACKET) {
			throw new JSONException("exepct '[', but " + JSONToken.name(lexer.token()) + ", " + lexer.info());
		}

		ObjectDeserializer deserializer = null;
		if (int.class == type) {
			deserializer = IntegerCodec.instance;
			lexer.nextToken(JSONToken.LITERAL_INT);
		} else if (String.class == type) {
			deserializer = StringCodec.instance;
			lexer.nextToken(JSONToken.LITERAL_STRING);
		} else {
			deserializer = config.getDeserializer(type);
			lexer.nextToken(deserializer.getFastMatchToken());
		}

		ParseContext context = this.context;
		this.setContext(array, fieldName);
		try {
			for (int i = 0;; ++i) {
				if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
					while (lexer.token() == JSONToken.COMMA) {
						lexer.nextToken();
						continue;
					}
				}

				if (lexer.token() == JSONToken.RBRACKET) {
					break;
				}

				if (int.class == type) {
					Object val = IntegerCodec.instance.deserialze(this, null, null);
					array.add(val);
				} else if (String.class == type) {
					String value;
					if (lexer.token() == JSONToken.LITERAL_STRING) {
						value = lexer.stringVal();
						lexer.nextToken(JSONToken.COMMA);
					} else {
						Object obj = this.parse();
						if (obj == null) {
							value = null;
						} else {
							value = obj.toString();
						}
					}

					array.add(value);
				} else {
					Object val;
					if (lexer.token() == JSONToken.NULL) {
						lexer.nextToken();
						val = null;
					} else {
						val = deserializer.deserialze(this, type, i);
					}
					array.add(val);
					checkListResolve(array);
				}

				if (lexer.token() == JSONToken.COMMA) {
					lexer.nextToken(deserializer.getFastMatchToken());
					continue;
				}
			}
		} finally {
			this.setContext(context);
		}

		lexer.nextToken(JSONToken.COMMA);
	}

	public Object[] parseArray(Type[] types) {
		if (lexer.token() == JSONToken.NULL) {
			lexer.nextToken(JSONToken.COMMA);
			return null;
		}

		if (lexer.token() != JSONToken.LBRACKET) {
			throw new JSONException("syntax error : " + lexer.tokenName());
		}

		Object[] list = new Object[types.length];
		if (types.length == 0) {
			lexer.nextToken(JSONToken.RBRACKET);

			if (lexer.token() != JSONToken.RBRACKET) {
				throw new JSONException("syntax error");
			}

			lexer.nextToken(JSONToken.COMMA);
			return new Object[0];
		}

		lexer.nextToken(JSONToken.LITERAL_INT);

		for (int i = 0; i < types.length; ++i) {
			Object value;

			if (lexer.token() == JSONToken.NULL) {
				value = null;
				lexer.nextToken(JSONToken.COMMA);
			} else {
				Type type = types[i];
				if (type == int.class || type == Integer.class) {
					if (lexer.token() == JSONToken.LITERAL_INT) {
						value = Integer.valueOf(lexer.intValue());
						lexer.nextToken(JSONToken.COMMA);
					} else {
						value = this.parse();
						value = TypeUtils.cast(value, type, config);
					}
				} else if (type == String.class) {
					if (lexer.token() == JSONToken.LITERAL_STRING) {
						value = lexer.stringVal();
						lexer.nextToken(JSONToken.COMMA);
					} else {
						value = this.parse();
						value = TypeUtils.cast(value, type, config);
					}
				} else {
					boolean isArray = false;
					Class<?> componentType = null;
					if (i == types.length - 1) {
						if (type instanceof Class) {
							Class<?> clazz = (Class<?>) type;
							isArray = clazz.isArray();
							componentType = clazz.getComponentType();
						}
					}

					// support varArgs
					if (isArray && lexer.token() != JSONToken.LBRACKET) {
						List<Object> varList = new ArrayList<Object>();

						ObjectDeserializer derializer = config.getDeserializer(componentType);
						int fastMatch = derializer.getFastMatchToken();

						if (lexer.token() != JSONToken.RBRACKET) {
							for (;;) {
								Object item = derializer.deserialze(this, type, null);
								varList.add(item);

								if (lexer.token() == JSONToken.COMMA) {
									lexer.nextToken(fastMatch);
								} else if (lexer.token() == JSONToken.RBRACKET) {
									break;
								} else {
									throw new JSONException("syntax error :" + JSONToken.name(lexer.token()));
								}
							}
						}

						value = TypeUtils.cast(varList, type, config);
					} else {
						ObjectDeserializer derializer = config.getDeserializer(type);
						value = derializer.deserialze(this, type, null);
					}
				}
			}
			list[i] = value;

			if (lexer.token() == JSONToken.RBRACKET) {
				break;
			}

			if (lexer.token() != JSONToken.COMMA) {
				throw new JSONException("syntax error :" + JSONToken.name(lexer.token()));
			}

			if (i == types.length - 1) {
				lexer.nextToken(JSONToken.RBRACKET);
			} else {
				lexer.nextToken(JSONToken.LITERAL_INT);
			}
		}

		if (lexer.token() != JSONToken.RBRACKET) {
			throw new JSONException("syntax error");
		}

		lexer.nextToken(JSONToken.COMMA);

		return list;
	}

	public void parseObject(Object object) {
		Class<?> clazz = object.getClass();
		JavaBeanDeserializer beanDeser = null;
		ObjectDeserializer deserizer = config.getDeserializer(clazz);
		if (deserizer instanceof JavaBeanDeserializer) {
			beanDeser = (JavaBeanDeserializer) deserizer;
		}

		if (lexer.token() != JSONToken.LBRACE && lexer.token() != JSONToken.COMMA) {
			throw new JSONException("syntax error, expect {, actual " + lexer.tokenName());
		}

		for (;;) {
			// lexer.scanSymbol
			String key = lexer.scanSymbol(symbolTable);

			if (key == null) {
				if (lexer.token() == JSONToken.RBRACE) {
					lexer.nextToken(JSONToken.COMMA);
					break;
				}
				if (lexer.token() == JSONToken.COMMA) {
					if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
						continue;
					}
				}
			}

			FieldDeserializer fieldDeser = null;
			if (beanDeser != null) {
				fieldDeser = beanDeser.getFieldDeserializer(key);
			}

			if (fieldDeser == null) {
				if (!lexer.isEnabled(Feature.IgnoreNotMatch)) {
					throw new JSONException("setter not found, class " + clazz.getName() + ", property " + key);
				}

				lexer.nextTokenWithColon();
				parse(); // skip

				if (lexer.token() == JSONToken.RBRACE) {
					lexer.nextToken();
					return;
				}

				continue;
			} else {
				Class<?> fieldClass = fieldDeser.fieldInfo.fieldClass;
				Type fieldType = fieldDeser.fieldInfo.fieldType;
				Object fieldValue;
				if (fieldClass == int.class) {
					lexer.nextTokenWithColon(JSONToken.LITERAL_INT);
					fieldValue = IntegerCodec.instance.deserialze(this, fieldType, null);
				} else if (fieldClass == String.class) {
					lexer.nextTokenWithColon(JSONToken.LITERAL_STRING);
					fieldValue = StringCodec.deserialze(this);
				} else if (fieldClass == long.class) {
					lexer.nextTokenWithColon(JSONToken.LITERAL_INT);
					fieldValue = LongCodec.instance.deserialze(this, fieldType, null);
				} else {
					ObjectDeserializer fieldValueDeserializer = config.getDeserializer(fieldClass, fieldType);

					lexer.nextTokenWithColon(fieldValueDeserializer.getFastMatchToken());
					fieldValue = fieldValueDeserializer.deserialze(this, fieldType, null);
				}

				fieldDeser.setValue(object, fieldValue);
			}

			if (lexer.token() == JSONToken.COMMA) {
				continue;
			}

			if (lexer.token() == JSONToken.RBRACE) {
				lexer.nextToken(JSONToken.COMMA);
				return;
			}
		}
	}

	public Object parseArrayWithType(Type collectionType) {
		if (lexer.token() == JSONToken.NULL) {
			lexer.nextToken();
			return null;
		}

		Type[] actualTypes = ((ParameterizedType) collectionType).getActualTypeArguments();

		if (actualTypes.length != 1) {
			throw new JSONException("not support type " + collectionType);
		}

		Type actualTypeArgument = actualTypes[0];

		if (actualTypeArgument instanceof Class) {
			List<Object> array = new ArrayList<Object>();
			this.parseArray((Class<?>) actualTypeArgument, array);
			return array;
		}

		if (actualTypeArgument instanceof WildcardType) {
			WildcardType wildcardType = (WildcardType) actualTypeArgument;

			// assert wildcardType.getUpperBounds().length == 1;
			Type upperBoundType = wildcardType.getUpperBounds()[0];

			// assert upperBoundType instanceof Class;
			if (Object.class.equals(upperBoundType)) {
				if (wildcardType.getLowerBounds().length == 0) {
					// Collection<?>
					return parse();
				} else {
					throw new JSONException("not support type : " + collectionType);
				}
			}

			List<Object> array = new ArrayList<Object>();
			this.parseArray((Class<?>) upperBoundType, array);
			return array;

			// throw new JSONException("not support type : " +
			// collectionType);return parse();
		}

		if (actualTypeArgument instanceof TypeVariable) {
			TypeVariable<?> typeVariable = (TypeVariable<?>) actualTypeArgument;
			Type[] bounds = typeVariable.getBounds();

			if (bounds.length != 1) {
				throw new JSONException("not support : " + typeVariable);
			}

			Type boundType = bounds[0];
			if (boundType instanceof Class) {
				List<Object> array = new ArrayList<Object>();
				this.parseArray((Class<?>) boundType, array);
				return array;
			}
		}

		if (actualTypeArgument instanceof ParameterizedType) {
			ParameterizedType parameterizedType = (ParameterizedType) actualTypeArgument;

			List<Object> array = new ArrayList<Object>();
			this.parseArray(parameterizedType, array);
			return array;
		}

		throw new JSONException("TODO : " + collectionType);
	}

	public void acceptType(String typeName) {
		JSONLexer lexer = this.lexer;

		lexer.nextTokenWithColon();

		if (lexer.token() != JSONToken.LITERAL_STRING) {
			throw new JSONException("type not match error");
		}

		if (typeName.equals(lexer.stringVal())) {
			lexer.nextToken();
			if (lexer.token() == JSONToken.COMMA) {
				lexer.nextToken();
			}
		} else {
			throw new JSONException("type not match error");
		}
	}

	public int getResolveStatus() {
		return resolveStatus;
	}

	public void setResolveStatus(int resolveStatus) {
		this.resolveStatus = resolveStatus;
	}

	public Object getObject(String path) {
		for (int i = 0; i < contextArrayIndex; ++i) {
			if (path.equals(contextArray[i].toString())) {
				return contextArray[i].object;
			}
		}

		return null;
	}

	@SuppressWarnings("rawtypes")
	public void checkListResolve(Collection array) {
		if (resolveStatus == NeedToResolve) {
			if (array instanceof List) {
				final int index = array.size() - 1;
				final List list = (List) array;
				ResolveTask task = getLastResolveTask();
				task.fieldDeserializer = new ResolveFieldDeserializer(this, list, index);
				task.ownerContext = context;
				setResolveStatus(DefaultJSONParser.NONE);
			} else {
				ResolveTask task = getLastResolveTask();
				task.fieldDeserializer = new ResolveFieldDeserializer(array);
				task.ownerContext = context;
				setResolveStatus(DefaultJSONParser.NONE);
			}
		}
	}

	@SuppressWarnings("rawtypes")
	public void checkMapResolve(Map object, Object fieldName) {
		if (resolveStatus == NeedToResolve) {
			ResolveFieldDeserializer fieldResolver = new ResolveFieldDeserializer(object, fieldName);
			ResolveTask task = getLastResolveTask();
			task.fieldDeserializer = fieldResolver;
			task.ownerContext = context;
			setResolveStatus(DefaultJSONParser.NONE);
		}
	}

	@SuppressWarnings("rawtypes")
	public Object parseObject(final Map object) {
		return parseObject(object, null);
	}

	public JSONObject parseObject() {
		JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
		object = (JSONObject) parseObject(object);
		return object;
	}

	@SuppressWarnings("rawtypes")
	public final void parseArray(final Collection array) {
		parseArray(array, null);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public final void parseArray(final Collection array, Object fieldName) {
		final JSONLexer lexer = this.lexer;

		if (lexer.token() == JSONToken.SET || lexer.token() == JSONToken.TREE_SET) {
			lexer.nextToken();
		}

		if (lexer.token() != JSONToken.LBRACKET) {
			throw new JSONException(
					"syntax error, expect [, actual " + JSONToken.name(lexer.token()) + ", pos " + lexer.pos());
		}

		lexer.nextToken(JSONToken.LITERAL_STRING);

		ParseContext context = this.context;
		this.setContext(array, fieldName);
		try {
			for (int i = 0;; ++i) {
				if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
					while (lexer.token() == JSONToken.COMMA) {
						lexer.nextToken();
						continue;
					}
				}

				Object value;
				switch (lexer.token()) {
				case LITERAL_INT:
					value = lexer.integerValue();
					lexer.nextToken(JSONToken.COMMA);
					break;
				case LITERAL_FLOAT:
					if (lexer.isEnabled(Feature.UseBigDecimal)) {
						value = lexer.decimalValue(true);
					} else {
						value = lexer.decimalValue(false);
					}
					lexer.nextToken(JSONToken.COMMA);
					break;
				case LITERAL_STRING:
					String stringLiteral = lexer.stringVal();
					lexer.nextToken(JSONToken.COMMA);

					if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
						JSONScanner iso8601Lexer = new JSONScanner(stringLiteral);
						if (iso8601Lexer.scanISO8601DateIfMatch()) {
							value = iso8601Lexer.getCalendar().getTime();
						} else {
							value = stringLiteral;
						}
						iso8601Lexer.close();
					} else {
						value = stringLiteral;
					}

					break;
				case TRUE:
					value = Boolean.TRUE;
					lexer.nextToken(JSONToken.COMMA);
					break;
				case FALSE:
					value = Boolean.FALSE;
					lexer.nextToken(JSONToken.COMMA);
					break;
				case LBRACE:
					JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
					value = parseObject(object, i);
					break;
				case LBRACKET:
					Collection items = new JSONArray();
					parseArray(items, i);
					if (lexer.isEnabled(Feature.UseObjectArray)) {
						value = items.toArray();
					} else {
						value = items;
					}
					break;
				case NULL:
					value = null;
					lexer.nextToken(JSONToken.LITERAL_STRING);
					break;
				case UNDEFINED:
					value = null;
					lexer.nextToken(JSONToken.LITERAL_STRING);
					break;
				case RBRACKET:
					lexer.nextToken(JSONToken.COMMA);
					return;
				case EOF:
					throw new JSONException("unclosed jsonArray");
				default:
					value = parse();
					break;
				}

				array.add(value);
				checkListResolve(array);

				if (lexer.token() == JSONToken.COMMA) {
					lexer.nextToken(JSONToken.LITERAL_STRING);
					continue;
				}
			}
		} finally {
			this.setContext(context);
		}
	}

	public ParseContext getContext() {
		return context;
	}

	public List<ResolveTask> getResolveTaskList() {
		if (resolveTaskList == null) {
			resolveTaskList = new ArrayList<ResolveTask>(2);
		}
		return resolveTaskList;
	}

	public void addResolveTask(ResolveTask task) {
		if (resolveTaskList == null) {
			resolveTaskList = new ArrayList<ResolveTask>(2);
		}
		resolveTaskList.add(task);
	}

	public ResolveTask getLastResolveTask() {
		return resolveTaskList.get(resolveTaskList.size() - 1);
	}

	public List<ExtraProcessor> getExtraProcessors() {
		if (extraProcessors == null) {
			extraProcessors = new ArrayList<ExtraProcessor>(2);
		}
		return extraProcessors;
	}

	public List<ExtraTypeProvider> getExtraTypeProviders() {
		if (extraTypeProviders == null) {
			extraTypeProviders = new ArrayList<ExtraTypeProvider>(2);
		}
		return extraTypeProviders;
	}

	public FieldTypeResolver getFieldTypeResolver() {
		return fieldTypeResolver;
	}

	public void setFieldTypeResolver(FieldTypeResolver fieldTypeResolver) {
		this.fieldTypeResolver = fieldTypeResolver;
	}

	public void setContext(ParseContext context) {
		if (lexer.isEnabled(Feature.DisableCircularReferenceDetect)) {
			return;
		}
		this.context = context;
	}

	public void popContext() {
		if (lexer.isEnabled(Feature.DisableCircularReferenceDetect)) {
			return;
		}

		this.context = this.context.parent;

		if (contextArrayIndex <= 0) {
			return;
		}

		contextArrayIndex--;
		contextArray[contextArrayIndex] = null;
	}

	public ParseContext setContext(Object object, Object fieldName) {
		if (lexer.isEnabled(Feature.DisableCircularReferenceDetect)) {
			return null;
		}

		return setContext(this.context, object, fieldName);
	}

	public ParseContext setContext(ParseContext parent, Object object, Object fieldName) {
		if (lexer.isEnabled(Feature.DisableCircularReferenceDetect)) {
			return null;
		}

		this.context = new ParseContext(parent, object, fieldName);
		addContext(this.context);

		return this.context;
	}

	private void addContext(ParseContext context) {
		int i = contextArrayIndex++;
		if (contextArray == null) {
			contextArray = new ParseContext[8];
		} else if (i >= contextArray.length) {
			int newLen = (contextArray.length * 3) / 2;
			ParseContext[] newArray = new ParseContext[newLen];
			System.arraycopy(contextArray, 0, newArray, 0, contextArray.length);
			contextArray = newArray;
		}
		contextArray[i] = context;
	}

	public Object parse() {
		return parse(null);
	}

	public Object parseKey() {
		if (lexer.token() == JSONToken.IDENTIFIER) {
			String value = lexer.stringVal();
			lexer.nextToken(JSONToken.COMMA);
			return value;
		}
		return parse(null);
	}

	public Object parse(Object fieldName) {
		final JSONLexer lexer = this.lexer;
		switch (lexer.token()) {
		case SET:
			lexer.nextToken();
			HashSet<Object> set = new HashSet<Object>();
			parseArray(set, fieldName);
			return set;
		case TREE_SET:
			lexer.nextToken();
			TreeSet<Object> treeSet = new TreeSet<Object>();
			parseArray(treeSet, fieldName);
			return treeSet;
		case LBRACKET:
			JSONArray array = new JSONArray();
			parseArray(array, fieldName);
			if (lexer.isEnabled(Feature.UseObjectArray)) {
				return array.toArray();
			}
			return array;
		case LBRACE:
			JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
			return parseObject(object, fieldName);
		case LITERAL_INT:
			Number intValue = lexer.integerValue();
			lexer.nextToken();
			return intValue;
		case LITERAL_FLOAT:
			Object value = lexer.decimalValue(lexer.isEnabled(Feature.UseBigDecimal));
			lexer.nextToken();
			return value;
		case LITERAL_STRING:
			String stringLiteral = lexer.stringVal();
			lexer.nextToken(JSONToken.COMMA);

			if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
				JSONScanner iso8601Lexer = new JSONScanner(stringLiteral);
				try {
					if (iso8601Lexer.scanISO8601DateIfMatch()) {
						return iso8601Lexer.getCalendar().getTime();
					}
				} finally {
					iso8601Lexer.close();
				}
			}

			return stringLiteral;
		case NULL:
			lexer.nextToken();
			return null;
		case UNDEFINED:
			lexer.nextToken();
			return null;
		case TRUE:
			lexer.nextToken();
			return Boolean.TRUE;
		case FALSE:
			lexer.nextToken();
			return Boolean.FALSE;
		case NEW:
			lexer.nextToken(JSONToken.IDENTIFIER);

			if (lexer.token() != JSONToken.IDENTIFIER) {
				throw new JSONException("syntax error");
			}
			lexer.nextToken(JSONToken.LPAREN);

			accept(JSONToken.LPAREN);
			long time = ((Number) lexer.integerValue()).longValue();
			accept(JSONToken.LITERAL_INT);

			accept(JSONToken.RPAREN);

			return new Date(time);
		case EOF:
			if (lexer.isBlankInput()) {
				return null;
			}
			throw new JSONException("unterminated json string, " + lexer.info());
		case ERROR:
		default:
			throw new JSONException("syntax error, " + lexer.info());
		}
	}

	public void config(Feature feature, boolean state) {
		this.lexer.config(feature, state);
	}

	public boolean isEnabled(Feature feature) {
		return lexer.isEnabled(feature);
	}

	public JSONLexer getLexer() {
		return lexer;
	}

	public final void accept(final int token) {
		final JSONLexer lexer = this.lexer;
		if (lexer.token() == token) {
			lexer.nextToken();
		} else {
			throw new JSONException(
					"syntax error, expect " + JSONToken.name(token) + ", actual " + JSONToken.name(lexer.token()));
		}
	}

	public final void accept(final int token, int nextExpectToken) {
		final JSONLexer lexer = this.lexer;
		if (lexer.token() == token) {
			lexer.nextToken(nextExpectToken);
		} else {
			throwException(token);
		}
	}

	public void throwException(int token) {
		throw new JSONException(
				"syntax error, expect " + JSONToken.name(token) + ", actual " + JSONToken.name(lexer.token()));
	}

	public void close() {
		final JSONLexer lexer = this.lexer;

		try {
			if (lexer.isEnabled(Feature.AutoCloseSource)) {
				if (lexer.token() != JSONToken.EOF) {
					throw new JSONException("not close json text, token : " + JSONToken.name(lexer.token()));
				}
			}
		} finally {
			lexer.close();
		}
	}

	public void handleResovleTask(Object value) {
		if (resolveTaskList == null) {
			return;
		}

		for (int i = 0, size = resolveTaskList.size(); i < size; ++i) {
			ResolveTask task = resolveTaskList.get(i);
			String ref = task.referenceValue;

			Object object = null;
			if (task.ownerContext != null) {
				object = task.ownerContext.object;
			}

			Object refValue = ref.startsWith("$") ? getObject(ref) : task.context.object;

			FieldDeserializer fieldDeser = task.fieldDeserializer;

			if (fieldDeser != null) {
				if (refValue != null && refValue.getClass() == JSONObject.class && fieldDeser.fieldInfo != null
						&& !Map.class.isAssignableFrom(fieldDeser.fieldInfo.fieldClass)) {
					Object root = this.contextArray[0].object;
					refValue = JSONPath.eval(root, ref);
				}

				fieldDeser.setValue(object, refValue);
			}
		}
	}

	public static class ResolveTask {

		public final ParseContext context;
		public final String referenceValue;
		public FieldDeserializer fieldDeserializer;
		public ParseContext ownerContext;

		public ResolveTask(ParseContext context, String referenceValue) {
			this.context = context;
			this.referenceValue = referenceValue;
		}
	}

	public void parseExtra(Object object, String key) {
		final JSONLexer lexer = this.lexer; // xxx
		lexer.nextTokenWithColon();
		Type type = null;

		if (extraTypeProviders != null) {
			for (ExtraTypeProvider extraProvider : extraTypeProviders) {
				type = extraProvider.getExtraType(object, key);
			}
		}
		Object value = type == null //
				? parse() // skip
				: parseObject(type);

		if (object instanceof ExtraProcessable) {
			ExtraProcessable extraProcessable = ((ExtraProcessable) object);
			extraProcessable.processExtra(key, value);
			return;
		}

		if (extraProcessors != null) {
			for (ExtraProcessor process : extraProcessors) {
				process.processExtra(object, key, value);
			}
		}

		if (resolveStatus == NeedToResolve) {
			resolveStatus = NONE;
		}
	}
}
